package com.ruoyi.project.system.controller;

import java.io.IOException;
import java.time.Duration;
import java.util.Date;
import java.util.List;
import java.util.Set;

import com.alibaba.fastjson2.JSONObject;
import com.aliyun.dingtalkcontact_1_0.Client;
import com.aliyun.dingtalkcontact_1_0.models.GetUserHeaders;
import com.aliyun.dingtalkcontact_1_0.models.GetUserResponse;
import com.aliyun.dingtalkoauth2_1_0.models.GetUserTokenRequest;
import com.aliyun.dingtalkoauth2_1_0.models.GetUserTokenResponse;
import com.aliyun.dysmsapi20170525.models.SendSmsResponse;
import com.aliyun.tea.TeaException;
import com.aliyun.teaopenapi.models.Config;
import com.aliyun.teautil.models.RuntimeOptions;
import com.github.pagehelper.util.StringUtil;
import com.ruoyi.framework.aspectj.lang.annotation.Anonymous;
import com.ruoyi.framework.aspectj.lang.annotation.Log;
import com.ruoyi.framework.aspectj.lang.enums.BusinessType;
import com.ruoyi.framework.redis.RedisCache;
import com.ruoyi.framework.web.domain.R;
import com.ruoyi.project.erp.dto.AuthCodeDTO;
import com.ruoyi.project.erp.dto.LoginPhoneReq;
import com.ruoyi.project.system.mapper.SysUserMapper;
import com.taobao.api.ApiException;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.core.text.Convert;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.framework.security.LoginBody;
import com.ruoyi.framework.security.LoginUser;
import com.ruoyi.framework.security.service.SysLoginService;
import com.ruoyi.framework.security.service.SysPermissionService;
import com.ruoyi.framework.security.service.TokenService;
import com.ruoyi.framework.web.domain.AjaxResult;
import com.ruoyi.project.system.domain.SysMenu;
import com.ruoyi.project.system.domain.SysUser;
import com.ruoyi.project.system.service.ISysConfigService;
import com.ruoyi.project.system.service.ISysMenuService;

import javax.security.auth.login.LoginException;
import javax.servlet.http.HttpServletResponse;

import static com.ruoyi.common.utils.SecurityUtils.getLoginUser;

/**
 * 登录验证
 * 
 * @author ruoyi
 */
@RestController
@Slf4j
public class SysLoginController
{
    @Autowired
    private SysLoginService loginService;

    @Autowired
    private ISysMenuService menuService;
    private final static String APP_AUTH_CODE = "APP_AUTH_CODE";

    private final static String APP_AUTH_PHONE_COUNT = "APP_AUTH_PHONE_COUNT";
    @Autowired
    private RedisCache redisCache;

    @Autowired
    private SysPermissionService permissionService;

    @Autowired
    private TokenService tokenService;
    
    @Autowired
    private ISysConfigService configService;

    @Autowired
    SysUserMapper sysUserMapper;

    /**
     * 登录方法
     * 
     * @param loginBody 登录信息
     * @return 结果
     */
    @PostMapping("/login")
    public AjaxResult login(@RequestBody LoginBody loginBody)
    {
        AjaxResult ajax = AjaxResult.success();
        // 生成令牌
        String token = loginService.login(loginBody.getUsername(), loginBody.getPassword(), loginBody.getCode(),
                loginBody.getUuid());
        ajax.put(Constants.TOKEN, token);
        return ajax;
    }


//    @RequestMapping("/dingRedirectUrl")
//    @Schema(description = "扫码登录回调")
//    public AjaxResult dingRedirectUrl(HttpServletResponse resp) throws IOException {
//        String targetUrl = "http://localhost:5173/appawake.html";
//        resp.sendRedirect(targetUrl);
//    }



    /**
     * 获取用户信息
     * 
     * @return 用户信息
     */
    @GetMapping("getInfo")
    public AjaxResult getInfo()
    {
        LoginUser loginUser = getLoginUser();
        SysUser user = loginUser.getUser();
        // 角色集合
        Set<String> roles = permissionService.getRolePermission(user);
        // 权限集合
        Set<String> permissions = permissionService.getMenuPermission(user);
        if (!loginUser.getPermissions().equals(permissions))
        {
            loginUser.setPermissions(permissions);
            tokenService.refreshToken(loginUser);
        }
        AjaxResult ajax = AjaxResult.success();
        ajax.put("user", user);
        ajax.put("roles", roles);
        ajax.put("permissions", permissions);
        ajax.put("isDefaultModifyPwd", initPasswordIsModify(user.getPwdUpdateDate()));
        ajax.put("isPasswordExpired", passwordIsExpiration(user.getPwdUpdateDate()));
        return ajax;
    }


    @Operation(summary = "获取验证码")
    @Anonymous
    @GetMapping("/sendCode")
    public R sendMessageToPhone(@RequestParam("phone") String phone) throws Exception {
        loginService.sendCode(phone);
        return R.ok();
    }


    @Operation(summary = "手机号验证码登录")
    @PostMapping("/phonelogin")
    public AjaxResult phonelogin(@Validated @RequestBody LoginPhoneReq loginReq) {
        AjaxResult ajax = AjaxResult.success();
        SysUser user = sysUserMapper.selectUserByPhonenumber(loginReq.getPhonenumber());
        String code = redisCache.getCacheObject(APP_AUTH_CODE + loginReq.getPhonenumber());
        log.info("redis提取的code为：~~~~~~~~~~~~~~~~~~~~~~~~~"+code);
        if (!code.equalsIgnoreCase(loginReq.getCode())) {
            throw new RuntimeException("验证码错误！");
        }
        // 生成令牌
        String token = loginService.loginByPhone(user); // <--- 注意这里，我们传的是整个user对象，而不是密码
        ajax.put(Constants.TOKEN, token);
        return ajax;
    }





    /**
     * 获取路由信息
     * 
     * @return 路由信息
     */
    @GetMapping("getRouters")
    public AjaxResult getRouters()
    {
        Long userId = SecurityUtils.getUserId();
        List<SysMenu> menus = menuService.selectMenuTreeByUserId(userId);
        return AjaxResult.success(menuService.buildMenus(menus));
    }
    
    // 检查初始密码是否提醒修改
    public boolean initPasswordIsModify(Date pwdUpdateDate)
    {
        Integer initPasswordModify = Convert.toInt(configService.selectConfigByKey("sys.account.initPasswordModify"));
        return initPasswordModify != null && initPasswordModify == 1 && pwdUpdateDate == null;
    }

    // 检查密码是否过期
    public boolean passwordIsExpiration(Date pwdUpdateDate)
    {
        Integer passwordValidateDays = Convert.toInt(configService.selectConfigByKey("sys.account.passwordValidateDays"));
        if (passwordValidateDays != null && passwordValidateDays > 0)
        {
            if (StringUtils.isNull(pwdUpdateDate))
            {
                // 如果从未修改过初始密码，直接提醒过期
                return true;
            }
            Date nowDate = DateUtils.getNowDate();
            return DateUtils.differentDaysByMillisecond(nowDate, pwdUpdateDate) > passwordValidateDays;
        }
        return false;
    }











    @Value("${app.clientId}")
    private String clientId;
    @Value("${app.clientSecret}")
    private String clientSecret;

    /**
     * 通过钉钉授权码获取用户信息
     * @param authCode 授权码
     * @return 用户信息或错误信息
     */

    // 生成令牌

    @PostMapping("/auth/code")
    public AjaxResult getUsers(@RequestBody AuthCodeDTO authCode) throws Exception {
        AjaxResult ajax = AjaxResult.success();
        ResponseEntity<String> tokenResp = getAccessToken(authCode.getAuthCode());
        if (!tokenResp.getStatusCode().is2xxSuccessful()) {
            JSONObject error = new JSONObject();
            error.put("error", tokenResp.getBody());
            return AjaxResult.error(error.toString());
        }
        String accessToken = tokenResp.getBody();
        ResponseEntity<JSONObject> userInfoResp = getUserInfo(accessToken);
        if (!userInfoResp.getStatusCode().is2xxSuccessful()) {
            return AjaxResult.error("获取用户信息失败: " + userInfoResp.getBody().getString("error"));
        }
        JSONObject userInfo = userInfoResp.getBody();
        // ====== 这里就是你要的取 unionId 的位置 ======
        String unionId = userInfo.getString("unionId");
        String token = loginService.loginDingTalk(unionId);
        ajax.put(Constants.TOKEN, token);
        return ajax;
    }

    /**
     * 获取钉钉accessToken
     * @param authCode 授权码
     * @return accessToken或错误信息
     */
    public ResponseEntity<String> getAccessToken(String authCode) {
        Config config = new Config();
        config.protocol = "https";
        config.regionId = "central";
        try {
            com.aliyun.dingtalkoauth2_1_0.Client client = new com.aliyun.dingtalkoauth2_1_0.Client(config);
            GetUserTokenRequest getUserTokenRequest = new GetUserTokenRequest()
                    .setClientId(clientId)
                    .setClientSecret(clientSecret)
                    .setCode(authCode)
                    .setGrantType("authorization_code");
            GetUserTokenResponse getUserTokenResponse = client.getUserToken(getUserTokenRequest);
            return ResponseEntity.ok(getUserTokenResponse.getBody().getAccessToken());
        } catch (Exception e) {
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("获取accessToken失败: " + e.getMessage());
        }
    }

    /**
     * 获取钉钉用户信息
     * @param accessToken accessToken
     * @return 用户信息或错误信息
     */
    public ResponseEntity<JSONObject> getUserInfo(String accessToken) {
        Config config = new Config();
        config.protocol = "https";
        config.regionId = "central";
        Client client;
        try {
            client = new Client(config);
        } catch (Exception e) {
            JSONObject error = new JSONObject();
            error.put("error", "初始化钉钉Client失败: " + e.getMessage());
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(error);
        }
        GetUserHeaders getUserHeaders = new GetUserHeaders();
        getUserHeaders.xAcsDingtalkAccessToken = accessToken;
        try {
            GetUserResponse resp = client.getUserWithOptions("me", getUserHeaders, new RuntimeOptions());
            JSONObject user = new JSONObject();
            user.put("unionId", resp.getBody().getUnionId());
            user.put("phone", resp.getBody().getMobile());
            user.put("nick", resp.getBody().getNick());
            user.put("stateCode", resp.getBody().getStateCode());
            return ResponseEntity.ok(user);
        } catch (TeaException err) {
            JSONObject error = new JSONObject();
            error.put("error", "钉钉接口异常: " + err.getMessage());
            return ResponseEntity.status(HttpStatus.BAD_GATEWAY).body(error);
        } catch (Exception _err) {
            JSONObject error = new JSONObject();
            error.put("error", "未知异常: " + _err.getMessage());
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(error);
        }
    }

    /**
     * 新增的方法：修改用户密码
     */
    @Log(title = "个人信息", businessType = BusinessType.UPDATE)
    @PutMapping("/updatePwd")
    public AjaxResult updatePwd(String oldPassword, String newPassword)
    {
        // 1. 获取当前登录用户的用户名
        LoginUser loginUser = getLoginUser();
        SysUser user1 = loginUser.getUser();

        // 2. 校验旧密码
        //    前端传来的旧密码 (oldPassword)
        //    数据库中存储的加密密码 (user.getPassword())
        if (!SecurityUtils.matchesPassword(oldPassword, user1.getPassword()))
        {
            return AjaxResult.error("修改密码失败，旧密码错误");
        }

        // 3. 校验新密码
        if (StringUtils.isEmpty(newPassword))
        {
            return AjaxResult.error("新密码不能为空");
        }
        if (newPassword.length() < 5 || newPassword.length() > 20)
        {
            return AjaxResult.error("密码长度必须在5到20个字符之间");
        }

        // 4. 检查新旧密码是否相同
        if (SecurityUtils.matchesPassword(newPassword, user1.getPassword()))
        {
            return AjaxResult.error("新密码不能与旧密码相同");
        }

        // 5. 调用 Service 层的方法来更新密码
        //    注意：传入的是加密后的新密码
        if (loginService.resetUserPwd(user1.getDdUserId(), SecurityUtils.encryptPassword(newPassword)) > 0)
        {
            // 更新缓存中的用户信息
            loginUser.getUser().setPassword(SecurityUtils.encryptPassword(newPassword));
            tokenService.setLoginUser(loginUser);
            return AjaxResult.success("密码修改成功");
        }

        return AjaxResult.error("修改密码异常，请联系管理员");
    }
}
